//////////
// 日志工具
//////////

#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <thread>
#include <unordered_map>
#include <sstream>
#include <memory>

#include <hello/conc/conc_queue.h>


namespace hello
{

// 日志级别
enum class log_level
{
    DEBUG = 0,
    INFO,
    WARN,
    ERROR
};


// 日志级别到字符串的映射，用于输出时打印级别信息
const std::unordered_map<log_level, const char*>
_log_level_string_map
{
    { log_level::DEBUG, "[DEBUG]"   },
    { log_level::INFO,  "[INFO]"    },
    { log_level::WARN,  "[WARN]"    },
    { log_level::ERROR, "[ERROR]"   }
};


// 日志输出工具父类
struct log_sink
{
    virtual ~log_sink() = default;

    // 输出字符串
    virtual void out(const std::string &) = 0;

    // 刷新输出缓冲
    virtual void flush() = 0;

    // 关闭输出流
    virtual void close() = 0;
};


// 标准日志输出
class stdout_sink : public log_sink
{
public:
    ~stdout_sink() = default;

public:
    void out(const std::string &_msg) override
    {
        std::cout << _msg << '\n';
    }

    void flush() override
    {
        std::cout.flush();
    }

    void close() override
    {
        std::cout << "std::cout cannot be closed..." << std::endl;
    }
};


// 文件日志输出
class file_sink : public log_sink
{
public:
    file_sink(std::string _file) : _filepath(std::move(_file))
    {
        _fout.open(_filepath, std::ios::app);
    }

public:
    ~file_sink()
    {
        if (_fout.is_open())
            _fout.close();
    }

public:
    void out(const std::string &_msg) override
    {
        _fout << _msg << '\n';
    }

    void flush() override
    {
        _fout.flush();
    }

    void close() override
    {
        _fout.close();
    }

    std::string get_filename() const noexcept
    {
        return _filepath;
    }

private:
    std::ofstream _fout;
    const std::string _filepath;
};


// 日志输出类，封装输出流及日志队列
class logger
{
private:
    logger() = default;

public:
    logger(const logger&) = delete;
    logger& operator = (const logger&) = delete;

    logger(logger&&) = default;
    logger& operator = (logger&&) = default;

public:
    inline static void add_stdout()
    {
        _log._sinks.emplace_back(std::make_shared<stdout_sink>());
    }

    inline static void add_outfile(std::string _filepath)
    {
        _log._sinks.emplace_back(std::make_shared<file_sink>(std::move(_filepath)));
    }

    inline static void set_log_level(log_level lvl)
    {
        _log._lvl = lvl;
    }

public:
    template<typename... Args>
    inline static void debug(Args&&... args)
    {
        out(log_level::DEBUG, std::forward<Args>(args)...);
    }

    template<typename... Args>
    inline static void info(Args&&... args)
    {
        out(log_level::INFO, std::forward<Args>(args)...);
    }

    template<typename... Args>
    inline static void warn(Args&&... args)
    {
        out(log_level::WARN, std::forward<Args>(args)...);
    }

    template<typename... Args>
    inline static void error(Args&&... args)
    {
        out(log_level::ERROR, std::forward<Args>(args)...);
    }
    
public:
    template<typename... Args>
    inline static void out(log_level lvl, Args&&... args)
    {
        if (lvl >= _log._lvl)
        {
            std::string _msg(_log_level_string_map.at(lvl));
            _msg.append(__build_message(args...));
            for (auto& p_sink : _log._sinks)
            {
                p_sink->out(_msg);
            }
        }
    }

private:
    template<typename T, typename... Args>
    inline static std::string __build_message(T first, Args&&... rest)
    {
        std::ostringstream _oss;
        __build_message_helper(_oss, first, rest...);
        return _oss.str();
    }

    template<typename T, typename... Args>
    inline static void __build_message_helper(std::ostringstream& oss, T first, Args&&... rest)
    {
        oss << first;
        if constexpr (sizeof...(rest) > 0)
        {
            oss << " ";
            __build_message_helper(oss, rest...);
        }
    }

private:
    std::vector<std::shared_ptr<log_sink>> _sinks;
    log_level _lvl = log_level::INFO;

private:
    static logger _log;
};

logger logger::_log;


// 日志类，提供日志工具访问接口
class default_log
{
};


}
